package lsp.client;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;

import util.SleepUtil;

import lsp.base.LspPacket;
import lsp.base.LspParams;
import lsp.base.MessageType;
import lsp.base.Pack;
import lsp.connector.AbstractConnector;

public class LspClient extends AbstractConnector implements ILspClient {
	
	private LspParams lspParams;
	private boolean waitingReponse = false;
	
	public LspClient(String serverHost, int serverPort, LspParams lspParams) throws IOException {
		super(InetAddress.getByName(serverHost), serverPort, lspParams);
		this.lspParams = lspParams;
		
		startServices();
		connectToServer();
	}
	
	public void connectToServer() throws SocketException {
		LspPacket lspPacket = new LspPacket(MessageType.CONNECT, (short) 0, (short) 0);
		sendPacket(lspPacket);
		
		waitAckConnect();
	}
	
	private void waitAckConnect() throws SocketException {
		long t0 = System.currentTimeMillis();
		while (!isConnected()) {
			if (System.currentTimeMillis() - t0 > lspParams.getEpoch()) {
				throw new SocketException("Erro ao tentar conectar");
			}
			
			SleepUtil.rest(20);
		}
	}

	@Override
	public short getConnId() {
		return getConnectionId();
	}

	@Override
	public byte[] read() {
		Pack pack = null;
		while (pack == null) {
			waitingReponse = true;
			pack = popReceivedExternalPack();
			if (pack == null) {
				SleepUtil.rest(20);
			}
		}
		return pack.getPayload();
	}

	@Override
	public void write(byte[] payload) {
		if (payload.length > 1000) {
			throw new IllegalArgumentException("O tamanho da mensagem deve ser menor que 1000 bytes.");
		}
		Pack pack = new Pack(getConnectionId(), payload);
		lspSender.sendData(pack);
	}
	
	@Override
	public void sendPacket(LspPacket lspPacket) {
		super.sendPacket(lspPacket);
		waitingReponse = true;
	}
	
	@Override
	public LspPacket popReceivedLspPacket() {
		if (!waitingReponse) {
			return null;
		}
		
		byte[] buf = new byte[1000];
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		try {
			senderSocket.receive(packet);
			return LspPacket.fromDatagramPacket(packet);
		} catch (SocketException e) {
			// socket closed: terminate execution
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@Override
	public void close() {
		setConnectionId((short) 0);
		senderSocket.close();
		super.close();
	}

}
